// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/trees/layer_tree_host.h"

#include <stdint.h>

#include "cc/animation/animation_curve.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/animation_player.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/element_animations.h"
#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/animation/scroll_offset_animations.h"
#include "cc/animation/timing_function.h"
#include "cc/animation/transform_operations.h"
#include "cc/base/completion_event.h"
#include "cc/base/time_util.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/transform_node.h"

namespace cc {
namespace {

    class LayerTreeHostAnimationTest : public LayerTreeTest {
    public:
        LayerTreeHostAnimationTest()
            : timeline_id_(AnimationIdProvider::NextTimelineId())
            , player_id_(AnimationIdProvider::NextPlayerId())
            , player_child_id_(AnimationIdProvider::NextPlayerId())
        {
            timeline_ = AnimationTimeline::Create(timeline_id_);
            player_ = AnimationPlayer::Create(player_id_);
            player_child_ = AnimationPlayer::Create(player_child_id_);

            player_->set_animation_delegate(this);
        }

        void AttachPlayersToTimeline()
        {
            animation_host()->AddAnimationTimeline(timeline_.get());
            layer_tree()->SetElementIdsForTesting();
            timeline_->AttachPlayer(player_.get());
            timeline_->AttachPlayer(player_child_.get());
        }

        void GetImplTimelineAndPlayerByID(const LayerTreeHostImpl& host_impl)
        {
            AnimationHost* animation_host_impl = GetImplAnimationHost(&host_impl);
            timeline_impl_ = animation_host_impl->GetTimelineById(timeline_id_);
            EXPECT_TRUE(timeline_impl_);
            player_impl_ = timeline_impl_->GetPlayerById(player_id_);
            EXPECT_TRUE(player_impl_);
            player_child_impl_ = timeline_impl_->GetPlayerById(player_child_id_);
            EXPECT_TRUE(player_child_impl_);
        }

        AnimationHost* GetImplAnimationHost(
            const LayerTreeHostImpl* host_impl) const
        {
            return static_cast<AnimationHost*>(host_impl->mutator_host());
        }

    protected:
        scoped_refptr<AnimationTimeline> timeline_;
        scoped_refptr<AnimationPlayer> player_;
        scoped_refptr<AnimationPlayer> player_child_;

        scoped_refptr<AnimationTimeline> timeline_impl_;
        scoped_refptr<AnimationPlayer> player_impl_;
        scoped_refptr<AnimationPlayer> player_child_impl_;

        const int timeline_id_;
        const int player_id_;
        const int player_child_id_;
    };

    // Makes sure that SetNeedsAnimate does not cause the CommitRequested() state to
    // be set.
    class LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested()
            : num_commits_(0)
        {
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void BeginMainFrame(const BeginFrameArgs& args) override
        {
            // We skip the first commit because its the commit that populates the
            // impl thread with a tree. After the second commit, the test is done.
            if (num_commits_ != 1)
                return;

            layer_tree_host()->SetNeedsAnimate();
            // Right now, CommitRequested is going to be true, because during
            // BeginFrame, we force CommitRequested to true to prevent requests from
            // hitting the impl thread. But, when the next DidCommit happens, we should
            // verify that CommitRequested has gone back to false.
        }

        void DidCommit() override
        {
            if (!num_commits_) {
                EXPECT_FALSE(layer_tree_host()->CommitRequested());
                layer_tree_host()->SetNeedsAnimate();
                EXPECT_FALSE(layer_tree_host()->CommitRequested());
            }

            // Verifies that the SetNeedsAnimate we made in ::Animate did not
            // trigger CommitRequested.
            EXPECT_FALSE(layer_tree_host()->CommitRequested());
            EndTest();
            num_commits_++;
        }

        void AfterTest() override { }

    private:
        int num_commits_;
    };

    MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested);

    // Trigger a frame with SetNeedsCommit. Then, inside the resulting animate
    // callback, request another frame using SetNeedsAnimate. End the test when
    // animate gets called yet-again, indicating that the proxy is correctly
    // handling the case where SetNeedsAnimate() is called inside the BeginFrame
    // flow.
    class LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback()
            : num_begin_frames_(0)
        {
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void BeginMainFrame(const BeginFrameArgs& args) override
        {
            if (!num_begin_frames_) {
                layer_tree_host()->SetNeedsAnimate();
                num_begin_frames_++;
                return;
            }
            EndTest();
        }

        void AfterTest() override { }

    private:
        int num_begin_frames_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback);

    // Add a layer animation and confirm that
    // LayerTreeHostImpl::UpdateAnimationState does get called.
    class LayerTreeHostAnimationTestAddAnimation
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestAddAnimation()
            : update_animation_state_was_called_(false)
        {
        }

        void BeginTest() override
        {
            AttachPlayersToTimeline();
            player_->AttachElement(layer_tree()->root_layer()->element_id());
            PostAddInstantAnimationToMainThreadPlayer(player_.get());
        }

        void UpdateAnimationState(LayerTreeHostImpl* host_impl,
            bool has_unfinished_animation) override
        {
            EXPECT_FALSE(has_unfinished_animation);
            update_animation_state_was_called_ = true;
        }

        void NotifyAnimationStarted(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            EXPECT_LT(base::TimeTicks(), monotonic_time);

            Animation* animation = player_->GetAnimation(TargetProperty::OPACITY);
            if (animation)
                player_->RemoveAnimation(animation->id());

            EndTest();
        }

        void AfterTest() override { EXPECT_TRUE(update_animation_state_was_called_); }

    private:
        bool update_animation_state_was_called_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAddAnimation);

    // Add a layer animation to a layer, but continually fail to draw. Confirm that
    // after a while, we do eventually force a draw.
    class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws()
            : started_animating_(false)
        {
        }

        void BeginTest() override
        {
            AttachPlayersToTimeline();
            player_->AttachElement(layer_tree()->root_layer()->element_id());
            PostAddAnimationToMainThreadPlayer(player_.get());
        }

        void AnimateLayers(LayerTreeHostImpl* host_impl,
            base::TimeTicks monotonic_time) override
        {
            started_animating_ = true;
        }

        void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (started_animating_)
                EndTest();
        }

        DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
            LayerTreeHostImpl::FrameData* frame,
            DrawResult draw_result) override
        {
            return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
        }

        void AfterTest() override { }

    private:
        bool started_animating_;
    };

    // Starvation can only be an issue with the MT compositor.
    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws);

    // Ensures that animations eventually get deleted.
    class LayerTreeHostAnimationTestAnimationsGetDeleted
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestAnimationsGetDeleted()
            : started_animating_(false)
        {
        }

        void BeginTest() override
        {
            AttachPlayersToTimeline();
            player_->AttachElement(layer_tree()->root_layer()->element_id());
            PostAddAnimationToMainThreadPlayer(player_.get());
        }

        void AnimateLayers(LayerTreeHostImpl* host_impl,
            base::TimeTicks monotonic_time) override
        {
            bool have_animations = !GetImplAnimationHost(host_impl)->ticking_players_for_testing().empty();
            if (!started_animating_ && have_animations) {
                started_animating_ = true;
                return;
            }

            if (started_animating_ && !have_animations)
                EndTest();
        }

        void NotifyAnimationFinished(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            // Animations on the impl-side ElementAnimations only get deleted during
            // a commit, so we need to schedule a commit.
            layer_tree_host()->SetNeedsCommit();
        }

        void AfterTest() override { }

    private:
        bool started_animating_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationsGetDeleted);

    // Ensure that an animation's timing function is respected.
    class LayerTreeHostAnimationTestAddAnimationWithTimingFunction
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            picture_ = FakePictureLayer::Create(&client_);
            picture_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(picture_->bounds());
            layer_tree()->root_layer()->AddChild(picture_);

            AttachPlayersToTimeline();
            player_child_->AttachElement(picture_->element_id());
        }

        void BeginTest() override
        {
            PostAddAnimationToMainThreadPlayer(player_child_.get());
        }

        void AnimateLayers(LayerTreeHostImpl* host_impl,
            base::TimeTicks monotonic_time) override
        {
            // TODO(ajuma): This test only checks the active tree. Add checks for
            // pending tree too.
            if (!host_impl->active_tree()->root_layer_for_testing())
                return;

            // Wait for the commit with the animation to happen.
            if (host_impl->sync_tree()->source_frame_number() != 0)
                return;

            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_child_impl = timeline_impl->GetPlayerById(player_child_id_);

            Animation* animation = player_child_impl->GetAnimation(TargetProperty::OPACITY);

            const FloatAnimationCurve* curve = animation->curve()->ToFloatAnimationCurve();
            float start_opacity = curve->GetValue(base::TimeDelta());
            float end_opacity = curve->GetValue(curve->Duration());
            float linearly_interpolated_opacity = 0.25f * end_opacity + 0.75f * start_opacity;
            base::TimeDelta time = TimeUtil::Scale(curve->Duration(), 0.25f);
            // If the linear timing function associated with this animation was not
            // picked up, then the linearly interpolated opacity would be different
            // because of the default ease timing function.
            EXPECT_FLOAT_EQ(linearly_interpolated_opacity, curve->GetValue(time));

            EndTest();
        }

        void AfterTest() override { }

        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> picture_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestAddAnimationWithTimingFunction);

    // Ensures that main thread animations have their start times synchronized with
    // impl thread animations.
    class LayerTreeHostAnimationTestSynchronizeAnimationStartTimes
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            picture_ = FakePictureLayer::Create(&client_);
            picture_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(picture_->bounds());

            layer_tree()->root_layer()->AddChild(picture_);

            AttachPlayersToTimeline();
            player_child_->set_animation_delegate(this);
            player_child_->AttachElement(picture_->element_id());
        }

        void BeginTest() override
        {
            PostAddAnimationToMainThreadPlayer(player_child_.get());
        }

        void NotifyAnimationStarted(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            Animation* animation = player_child_->GetAnimation(TargetProperty::OPACITY);
            main_start_time_ = animation->start_time();
            player_child_->RemoveAnimation(animation->id());
            EndTest();
        }

        void UpdateAnimationState(LayerTreeHostImpl* impl_host,
            bool has_unfinished_animation) override
        {
            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(impl_host)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_child_impl = timeline_impl->GetPlayerById(player_child_id_);

            Animation* animation = player_child_impl->GetAnimation(TargetProperty::OPACITY);
            if (!animation)
                return;

            impl_start_time_ = animation->start_time();
        }

        void AfterTest() override
        {
            EXPECT_EQ(impl_start_time_, main_start_time_);
            EXPECT_LT(base::TimeTicks(), impl_start_time_);
        }

    private:
        base::TimeTicks main_start_time_;
        base::TimeTicks impl_start_time_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> picture_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestSynchronizeAnimationStartTimes);

    // Ensures that notify animation finished is called.
    class LayerTreeHostAnimationTestAnimationFinishedEvents
        : public LayerTreeHostAnimationTest {
    public:
        void BeginTest() override
        {
            AttachPlayersToTimeline();
            player_->AttachElement(layer_tree()->root_layer()->element_id());
            PostAddInstantAnimationToMainThreadPlayer(player_.get());
        }

        void NotifyAnimationFinished(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            Animation* animation = player_->GetAnimation(TargetProperty::OPACITY);
            if (animation)
                player_->RemoveAnimation(animation->id());
            EndTest();
        }

        void AfterTest() override { }
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestAnimationFinishedEvents);

    // Ensures that when opacity is being animated, this value does not cause the
    // subtree to be skipped.
    class LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity()
            : update_check_layer_()
        {
        }

        void SetupTree() override
        {
            update_check_layer_ = FakePictureLayer::Create(&client_);
            update_check_layer_->SetOpacity(0.f);
            layer_tree()->SetRootLayer(update_check_layer_);
            client_.set_bounds(update_check_layer_->bounds());
            LayerTreeHostAnimationTest::SetupTree();

            AttachPlayersToTimeline();
            player_->AttachElement(update_check_layer_->element_id());
        }

        void BeginTest() override
        {
            PostAddAnimationToMainThreadPlayer(player_.get());
        }

        void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override
        {
            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_impl = timeline_impl->GetPlayerById(player_id_);

            Animation* animation_impl = player_impl->GetAnimation(TargetProperty::OPACITY);
            player_impl->RemoveAnimation(animation_impl->id());
            EndTest();
        }

        void AfterTest() override
        {
            // Update() should have been called once, proving that the layer was not
            // skipped.
            EXPECT_EQ(1, update_check_layer_->update_count());

            // clear update_check_layer_ so LayerTreeHost dies.
            update_check_layer_ = NULL;
        }

    private:
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> update_check_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity);

    // Layers added to tree with existing active animations should have the
    // animation correctly recognized.
    class LayerTreeHostAnimationTestLayerAddedWithAnimation
        : public LayerTreeHostAnimationTest {
    public:
        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                AttachPlayersToTimeline();

                scoped_refptr<Layer> layer = Layer::Create();
                layer->SetElementId(ElementId(42, 0));
                player_->AttachElement(layer->element_id());
                player_->set_animation_delegate(this);

                // Any valid AnimationCurve will do here.
                std::unique_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve());
                std::unique_ptr<Animation> animation(
                    Animation::Create(std::move(curve), 1, 1, TargetProperty::OPACITY));
                player_->AddAnimation(std::move(animation));

                // We add the animation *before* attaching the layer to the tree.
                layer_tree()->root_layer()->AddChild(layer);
            }
        }

        void AnimateLayers(LayerTreeHostImpl* impl_host,
            base::TimeTicks monotonic_time) override
        {
            EndTest();
        }

        void AfterTest() override { }
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestLayerAddedWithAnimation);

    class LayerTreeHostAnimationTestCancelAnimateCommit
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestCancelAnimateCommit()
            : num_begin_frames_(0)
            , num_commit_calls_(0)
            , num_draw_calls_(0)
        {
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void BeginMainFrame(const BeginFrameArgs& args) override
        {
            num_begin_frames_++;
            // No-op animate will cancel the commit.
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                EndTest();
                return;
            }
            layer_tree_host()->SetNeedsAnimate();
        }

        void CommitCompleteOnThread(LayerTreeHostImpl* impl) override
        {
            num_commit_calls_++;
            if (impl->active_tree()->source_frame_number() > 1)
                FAIL() << "Commit should have been canceled.";
        }

        void DrawLayersOnThread(LayerTreeHostImpl* impl) override
        {
            num_draw_calls_++;
            if (impl->active_tree()->source_frame_number() > 1)
                FAIL() << "Draw should have been canceled.";
        }

        void AfterTest() override
        {
            EXPECT_EQ(2, num_begin_frames_);
            EXPECT_EQ(1, num_commit_calls_);
            EXPECT_EQ(1, num_draw_calls_);
        }

    private:
        int num_begin_frames_;
        int num_commit_calls_;
        int num_draw_calls_;
    };

    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCancelAnimateCommit);

    class LayerTreeHostAnimationTestForceRedraw
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestForceRedraw()
            : num_animate_(0)
            , num_draw_layers_(0)
        {
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void BeginMainFrame(const BeginFrameArgs& args) override
        {
            if (++num_animate_ < 2)
                layer_tree_host()->SetNeedsAnimate();
        }

        void UpdateLayerTreeHost() override
        {
            layer_tree_host()->SetNextCommitForcesRedraw();
        }

        void DrawLayersOnThread(LayerTreeHostImpl* impl) override
        {
            if (++num_draw_layers_ == 2)
                EndTest();
        }

        void AfterTest() override
        {
            // The first commit will always draw; make sure the second draw triggered
            // by the animation was not cancelled.
            EXPECT_EQ(2, num_draw_layers_);
            EXPECT_EQ(2, num_animate_);
        }

    private:
        int num_animate_;
        int num_draw_layers_;
    };

    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestForceRedraw);

    class LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit()
            : num_animate_(0)
            , num_draw_layers_(0)
        {
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void BeginMainFrame(const BeginFrameArgs& args) override
        {
            if (++num_animate_ <= 2) {
                layer_tree_host()->SetNeedsCommit();
                layer_tree_host()->SetNeedsAnimate();
            }
        }

        void DrawLayersOnThread(LayerTreeHostImpl* impl) override
        {
            if (++num_draw_layers_ == 2)
                EndTest();
        }

        void AfterTest() override
        {
            // The first commit will always draw; make sure the second draw triggered
            // by the SetNeedsCommit was not cancelled.
            EXPECT_EQ(2, num_draw_layers_);
            EXPECT_GE(num_animate_, 2);
        }

    private:
        int num_animate_;
        int num_draw_layers_;
    };

    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit);

    // Animations should not be started when frames are being skipped due to
    // checkerboard.
    class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations
        : public LayerTreeHostAnimationTest {
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            picture_ = FakePictureLayer::Create(&client_);
            picture_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(picture_->bounds());
            layer_tree()->root_layer()->AddChild(picture_);

            AttachPlayersToTimeline();
            player_child_->AttachElement(picture_->element_id());
            player_child_->set_animation_delegate(this);
        }

        void InitializeSettings(LayerTreeSettings* settings) override
        {
            // Make sure that drawing many times doesn't cause a checkerboarded
            // animation to start so we avoid flake in this test.
            settings->timeout_and_draw_when_animation_checkerboards = false;
            LayerTreeHostAnimationTest::InitializeSettings(settings);
        }

        void BeginTest() override
        {
            prevented_draw_ = 0;
            started_times_ = 0;

            PostSetNeedsCommitToMainThread();
        }

        DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
            LayerTreeHostImpl::FrameData* frame_data,
            DrawResult draw_result) override
        {
            // Don't checkerboard when the first animation wants to start.
            if (host_impl->active_tree()->source_frame_number() < 2)
                return draw_result;
            if (TestEnded())
                return draw_result;
            // Act like there is checkerboard when the second animation wants to draw.
            ++prevented_draw_;
            if (prevented_draw_ > 2)
                EndTest();
            return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
        }

        void DidCommitAndDrawFrame() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1:
                // The animation is longer than 1 BeginFrame interval.
                AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 0.2f, 0.8f,
                    false);
                break;
            case 2:
                // This second animation will not be drawn so it should not start.
                AddAnimatedTransformToPlayer(player_child_.get(), 0.1, 5, 5);
                break;
            }
        }

        void NotifyAnimationStarted(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            if (TestEnded())
                return;
            started_times_++;
        }

        void AfterTest() override
        {
            // Make sure we tried to draw the second animation but failed.
            EXPECT_LT(0, prevented_draw_);
            // The first animation should be started, but the second should not because
            // of checkerboard.
            EXPECT_EQ(1, started_times_);
        }

        int prevented_draw_;
        int started_times_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> picture_;
    };

    MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations);

    // Verifies that scroll offset animations are only accepted when impl-scrolling
    // is supported, and that when scroll offset animations are accepted,
    // scroll offset updates are sent back to the main thread.
    class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();

            scroll_layer_ = FakePictureLayer::Create(&client_);
            scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
            scroll_layer_->SetBounds(gfx::Size(1000, 1000));
            client_.set_bounds(scroll_layer_->bounds());
            scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
            layer_tree()->root_layer()->AddChild(scroll_layer_);

            AttachPlayersToTimeline();
            player_child_->AttachElement(scroll_layer_->element_id());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1: {
                std::unique_ptr<ScrollOffsetAnimationCurve> curve(
                    ScrollOffsetAnimationCurve::Create(
                        gfx::ScrollOffset(500.f, 550.f),
                        CubicBezierTimingFunction::CreatePreset(
                            CubicBezierTimingFunction::EaseType::EASE_IN_OUT)));
                std::unique_ptr<Animation> animation(Animation::Create(
                    std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET));
                animation->set_needs_synchronized_start_time(true);
                bool impl_scrolling_supported = proxy()->SupportsImplScrolling();
                if (impl_scrolling_supported)
                    player_child_->AddAnimation(std::move(animation));
                else
                    EndTest();
                break;
            }
            default:
                if (scroll_layer_->scroll_offset().x() > 10 && scroll_layer_->scroll_offset().y() > 20)
                    EndTest();
            }
        }

        void AfterTest() override { }

    private:
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> scroll_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestScrollOffsetChangesArePropagated);

    // Verifies that when a main thread scrolling reason gets added, the
    // notification to takover the animation on the main thread gets sent.
    class LayerTreeHostAnimationTestScrollOffsetAnimationTakeover
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestScrollOffsetAnimationTakeover() { }

        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();

            scroll_layer_ = FakePictureLayer::Create(&client_);
            scroll_layer_->SetBounds(gfx::Size(10000, 10000));
            client_.set_bounds(scroll_layer_->bounds());
            scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
            scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
            layer_tree()->root_layer()->AddChild(scroll_layer_);

            AttachPlayersToTimeline();
            player_child_->AttachElement(scroll_layer_->element_id());
            // Allows NotifyAnimationTakeover to get called.
            player_child_->set_animation_delegate(this);
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                // Add an update after the first commit to trigger the animation takeover
                // path.
                animation_host()->scroll_offset_animations().AddTakeoverUpdate(
                    scroll_layer_->element_id());
                EXPECT_TRUE(
                    animation_host()->scroll_offset_animations().HasUpdatesForTesting());
            }
        }

        void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->sync_tree()->source_frame_number() == 0) {
                GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate(
                    scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f),
                    gfx::ScrollOffset(10, 20), base::TimeDelta());
            }
        }

        void NotifyAnimationTakeover(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            double animation_start_time,
            std::unique_ptr<AnimationCurve> curve) override
        {
            EndTest();
        }

        void AfterTest() override { }

    private:
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> scroll_layer_;
    };

    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationTakeover);

    // Verifies that an impl-only scroll offset animation gets updated when the
    // scroll offset is adjusted on the main thread.
    class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted() { }

        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();

            scroll_layer_ = FakePictureLayer::Create(&client_);
            scroll_layer_->SetBounds(gfx::Size(10000, 10000));
            client_.set_bounds(scroll_layer_->bounds());
            scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
            scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
            layer_tree()->root_layer()->AddChild(scroll_layer_);

            AttachPlayersToTimeline();
        }

        AnimationPlayer& ScrollOffsetPlayer(
            const LayerTreeHostImpl& host_impl,
            scoped_refptr<FakePictureLayer> layer) const
        {
            scoped_refptr<ElementAnimations> element_animations = GetImplAnimationHost(&host_impl)
                                                                      ->GetElementAnimationsForElementId(layer->element_id());
            DCHECK(element_animations);
            DCHECK(element_animations->players_list().might_have_observers());
            AnimationPlayer* player = &*element_animations->players_list().begin();
            DCHECK(player);
            return *player;
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                // Add an update after the first commit to trigger the animation update
                // path.
                animation_host()->scroll_offset_animations().AddAdjustmentUpdate(
                    scroll_layer_->element_id(), gfx::Vector2dF(100.f, 100.f));
                EXPECT_TRUE(
                    animation_host()->scroll_offset_animations().HasUpdatesForTesting());
            } else if (layer_tree_host()->SourceFrameNumber() == 2) {
                // Verify that the update queue is cleared after the update is applied.
                EXPECT_FALSE(
                    animation_host()->scroll_offset_animations().HasUpdatesForTesting());
            }
        }

        void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override
        {
            // Note that the frame number gets incremented after BeginCommitOnThread but
            // before WillCommitCompleteOnThread and CommitCompleteOnThread.
            if (host_impl->sync_tree()->source_frame_number() == 0) {
                GetImplTimelineAndPlayerByID(*host_impl);
                // This happens after the impl-only animation is added in
                // WillCommitCompleteOnThread.
                Animation* animation = ScrollOffsetPlayer(*host_impl, scroll_layer_)
                                           .GetAnimation(TargetProperty::SCROLL_OFFSET);
                DCHECK(animation);
                ScrollOffsetAnimationCurve* curve = animation->curve()->ToScrollOffsetAnimationCurve();

                // Verifiy the initial and target position before the scroll offset
                // update from MT.
                EXPECT_EQ(Animation::RunState::RUNNING, animation->run_state());
                EXPECT_EQ(gfx::ScrollOffset(10.f, 20.f),
                    curve->GetValue(base::TimeDelta()));
                EXPECT_EQ(gfx::ScrollOffset(650.f, 750.f), curve->target_value());
            }
        }

        void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->sync_tree()->source_frame_number() == 0) {
                GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate(
                    scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f),
                    gfx::ScrollOffset(10, 20), base::TimeDelta());
            }
        }

        void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->sync_tree()->source_frame_number() == 1) {
                Animation* animation = ScrollOffsetPlayer(*host_impl, scroll_layer_)
                                           .GetAnimation(TargetProperty::SCROLL_OFFSET);
                DCHECK(animation);
                ScrollOffsetAnimationCurve* curve = animation->curve()->ToScrollOffsetAnimationCurve();
                // Verifiy the initial and target position after the scroll offset
                // update from MT
                EXPECT_EQ(Animation::RunState::STARTING, animation->run_state());
                EXPECT_EQ(gfx::ScrollOffset(110.f, 120.f),
                    curve->GetValue(base::TimeDelta()));
                EXPECT_EQ(gfx::ScrollOffset(750.f, 850.f), curve->target_value());

                EndTest();
            }
        }

        void AfterTest() override { }

    private:
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> scroll_layer_;
    };

    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted);

    // Verifies that when the main thread removes a scroll animation and sets a new
    // scroll position, the active tree takes on exactly this new scroll position
    // after activation, and the main thread doesn't receive a spurious scroll
    // delta.
    class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestScrollOffsetAnimationRemoval()
            : final_postion_(50.0, 100.0)
        {
        }

        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();

            scroll_layer_ = FakePictureLayer::Create(&client_);
            scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
            scroll_layer_->SetBounds(gfx::Size(10000, 10000));
            client_.set_bounds(scroll_layer_->bounds());
            scroll_layer_->SetScrollOffset(gfx::ScrollOffset(100.0, 200.0));
            layer_tree()->root_layer()->AddChild(scroll_layer_);

            std::unique_ptr<ScrollOffsetAnimationCurve> curve(
                ScrollOffsetAnimationCurve::Create(
                    gfx::ScrollOffset(6500.f, 7500.f),
                    CubicBezierTimingFunction::CreatePreset(
                        CubicBezierTimingFunction::EaseType::EASE_IN_OUT)));
            std::unique_ptr<Animation> animation(Animation::Create(
                std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET));
            animation->set_needs_synchronized_start_time(true);

            AttachPlayersToTimeline();
            player_child_->AttachElement(scroll_layer_->element_id());
            player_child_->AddAnimation(std::move(animation));
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void BeginMainFrame(const BeginFrameArgs& args) override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 0:
                break;
            case 1: {
                Animation* animation = player_child_->GetAnimation(TargetProperty::SCROLL_OFFSET);
                player_child_->RemoveAnimation(animation->id());
                scroll_layer_->SetScrollOffset(final_postion_);
                break;
            }
            default:
                EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset());
            }
        }

        void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override
        {
            host_impl->BlockNotifyReadyToActivateForTesting(
                ShouldBlockActivation(host_impl));
        }

        void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
            const BeginFrameArgs& args) override
        {
            host_impl->BlockNotifyReadyToActivateForTesting(
                ShouldBlockActivation(host_impl));
        }

        void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->pending_tree()->source_frame_number() != 1)
                return;
            LayerImpl* scroll_layer_impl = host_impl->pending_tree()->LayerById(scroll_layer_->id());
            EXPECT_EQ(final_postion_, scroll_layer_impl->CurrentScrollOffset());
        }

        void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->active_tree()->source_frame_number() != 1)
                return;
            LayerImpl* scroll_layer_impl = host_impl->active_tree()->LayerById(scroll_layer_->id());
            EXPECT_EQ(final_postion_, scroll_layer_impl->CurrentScrollOffset());
            EndTest();
        }

        void AfterTest() override
        {
            EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset());
        }

    private:
        bool ShouldBlockActivation(LayerTreeHostImpl* host_impl)
        {
            if (!host_impl->pending_tree())
                return false;

            if (!host_impl->active_tree()->root_layer_for_testing())
                return false;

            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_impl = timeline_impl->GetPlayerById(player_child_id_);

            LayerImpl* scroll_layer_impl = host_impl->active_tree()->LayerById(scroll_layer_->id());
            Animation* animation = player_impl->GetAnimation(TargetProperty::SCROLL_OFFSET);

            if (!animation || animation->run_state() != Animation::RUNNING)
                return false;

            // Block activation until the running animation has a chance to produce a
            // scroll delta.
            gfx::Vector2dF scroll_delta = ScrollDelta(scroll_layer_impl);
            if (scroll_delta.x() > 0.f || scroll_delta.y() > 0.f)
                return false;

            return true;
        }

        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> scroll_layer_;
        const gfx::ScrollOffset final_postion_;
    };

    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationRemoval);

    // When animations are simultaneously added to an existing layer and to a new
    // layer, they should start at the same time, even when there's already a
    // running animation on the existing layer.
    class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers()
            : frame_count_with_pending_tree_(0)
        {
        }

        void BeginTest() override
        {
            AttachPlayersToTimeline();
            PostSetNeedsCommitToMainThread();
        }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                player_->AttachElement(layer_tree()->root_layer()->element_id());
                AddAnimatedTransformToPlayer(player_.get(), 4, 1, 1);
            } else if (layer_tree_host()->SourceFrameNumber() == 2) {
                AddOpacityTransitionToPlayer(player_.get(), 1, 0.f, 0.5f, true);

                scoped_refptr<Layer> layer = Layer::Create();
                layer_tree()->root_layer()->AddChild(layer);

                layer_tree()->SetElementIdsForTesting();
                layer->SetBounds(gfx::Size(4, 4));

                player_child_->AttachElement(layer->element_id());
                player_child_->set_animation_delegate(this);
                AddOpacityTransitionToPlayer(player_child_.get(), 1, 0.f, 0.5f, true);
            }
        }

        void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override
        {
            host_impl->BlockNotifyReadyToActivateForTesting(true);
        }

        void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            // For the commit that added animations to new and existing layers, keep
            // blocking activation. We want to verify that even with activation blocked,
            // the animation on the layer that's already in the active tree won't get a
            // head start.
            if (host_impl->pending_tree()->source_frame_number() != 2) {
                host_impl->BlockNotifyReadyToActivateForTesting(false);
            }
        }

        void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
            const BeginFrameArgs& args) override
        {
            if (!host_impl->pending_tree() || host_impl->pending_tree()->source_frame_number() != 2)
                return;

            frame_count_with_pending_tree_++;
            if (frame_count_with_pending_tree_ == 2) {
                host_impl->BlockNotifyReadyToActivateForTesting(false);
            }
        }

        void UpdateAnimationState(LayerTreeHostImpl* host_impl,
            bool has_unfinished_animation) override
        {
            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_impl = timeline_impl->GetPlayerById(player_id_);
            scoped_refptr<AnimationPlayer> player_child_impl = timeline_impl->GetPlayerById(player_child_id_);

            // wait for tree activation.
            if (!player_impl->element_animations())
                return;

            Animation* root_animation = player_impl->GetAnimation(TargetProperty::OPACITY);
            if (!root_animation || root_animation->run_state() != Animation::RUNNING)
                return;

            Animation* child_animation = player_child_impl->GetAnimation(TargetProperty::OPACITY);
            EXPECT_EQ(Animation::RUNNING, child_animation->run_state());
            EXPECT_EQ(root_animation->start_time(), child_animation->start_time());
            player_impl->AbortAnimations(TargetProperty::OPACITY, false);
            player_impl->AbortAnimations(TargetProperty::TRANSFORM, false);
            player_child_impl->AbortAnimations(TargetProperty::OPACITY, false);
            EndTest();
        }

        void AfterTest() override { }

    private:
        int frame_count_with_pending_tree_;
    };

    // This test blocks activation which is not supported for single thread mode.
    MULTI_THREAD_BLOCKNOTIFY_TEST_F(
        LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers);

    class LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();

            layer_ = FakePictureLayer::Create(&client_);
            layer_->SetBounds(gfx::Size(2, 2));
            client_.set_bounds(layer_->bounds());
            // Transform the layer to 4,4 to start.
            gfx::Transform start_transform;
            start_transform.Translate(4.0, 4.0);
            layer_->SetTransform(start_transform);

            layer_tree()->root_layer()->AddChild(layer_);
            layer_tree()->SetElementIdsForTesting();

            player_->AttachElement(layer_->element_id());

            AttachPlayersToTimeline();
        }

        void BeginTest() override
        {
            // Add a translate from 6,7 to 8,9.
            TransformOperations start;
            start.AppendTranslate(6.f, 7.f, 0.f);
            TransformOperations end;
            end.AppendTranslate(8.f, 9.f, 0.f);
            AddAnimatedTransformToPlayer(player_.get(), 4.0, start, end);

            PostSetNeedsCommitToMainThread();
        }

        void WillPrepareTilesOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->sync_tree()->source_frame_number() != 0)
                return;

            // After checking this on the sync tree, we will activate, which will cause
            // PrepareTiles to happen again (which races with the test exiting).
            if (TestEnded())
                return;

            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_impl = timeline_impl->GetPlayerById(player_id_);

            LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id());
            Animation* animation = player_impl->GetAnimation(TargetProperty::TRANSFORM);

            // The animation should be starting for the first frame.
            EXPECT_EQ(Animation::STARTING, animation->run_state());

            // And the transform should be propogated to the sync tree layer, at its
            // starting state which is 6,7.
            gfx::Transform expected_transform;
            expected_transform.Translate(6.0, 7.0);
            EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, child->DrawTransform());
            // And the sync tree layer should know it is animating.
            EXPECT_TRUE(child->screen_space_transform_is_animating());

            player_impl->AbortAnimations(TargetProperty::TRANSFORM, false);
            EndTest();
        }

        void AfterTest() override { }

        FakeContentLayerClient client_;
        scoped_refptr<Layer> layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit);

    // When a layer with an animation is removed from the tree and later re-added,
    // the animation should resume.
    class LayerTreeHostAnimationTestAnimatedLayerRemovedAndAdded
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            layer_ = Layer::Create();
            layer_->SetBounds(gfx::Size(4, 4));
            layer_tree()->root_layer()->AddChild(layer_);

            layer_tree()->SetElementIdsForTesting();

            animation_host()->AddAnimationTimeline(timeline_.get());
            timeline_->AttachPlayer(player_.get());
            player_->AttachElement(layer_->element_id());
            DCHECK(player_->element_animations());

            AddOpacityTransitionToPlayer(player_.get(), 10000.0, 0.1f, 0.9f, true);
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 0:
                EXPECT_TRUE(
                    player_->element_animations()->has_element_in_active_list());
                EXPECT_FALSE(
                    player_->element_animations()->has_element_in_pending_list());
                EXPECT_TRUE(animation_host()->NeedsTickAnimations());
                break;
            case 1:
                layer_->RemoveFromParent();
                EXPECT_FALSE(
                    player_->element_animations()->has_element_in_active_list());
                EXPECT_FALSE(
                    player_->element_animations()->has_element_in_pending_list());
                EXPECT_FALSE(animation_host()->NeedsTickAnimations());
                break;
            case 2:
                layer_tree()->root_layer()->AddChild(layer_);
                EXPECT_TRUE(
                    player_->element_animations()->has_element_in_active_list());
                EXPECT_FALSE(
                    player_->element_animations()->has_element_in_pending_list());
                EXPECT_TRUE(animation_host()->NeedsTickAnimations());
                break;
            }
        }

        void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override
        {
            scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
            scoped_refptr<AnimationPlayer> player_impl = timeline_impl->GetPlayerById(player_id_);

            switch (host_impl->active_tree()->source_frame_number()) {
            case 0:
                EXPECT_TRUE(
                    player_impl->element_animations()->has_element_in_active_list());
                EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsTickAnimations());
                break;
            case 1:
                EXPECT_FALSE(
                    player_impl->element_animations()->has_element_in_active_list());
                EXPECT_FALSE(GetImplAnimationHost(host_impl)->NeedsTickAnimations());
                break;
            case 2:
                EXPECT_TRUE(
                    player_impl->element_animations()->has_element_in_active_list());
                EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsTickAnimations());
                EndTest();
                break;
            }
        }

        void AfterTest() override { }

    private:
        scoped_refptr<Layer> layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestAnimatedLayerRemovedAndAdded);

    class LayerTreeHostAnimationTestAddAnimationAfterAnimating
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            layer_ = Layer::Create();
            layer_->SetBounds(gfx::Size(4, 4));
            layer_tree()->root_layer()->AddChild(layer_);

            AttachPlayersToTimeline();

            player_->AttachElement(layer_tree()->root_layer()->element_id());
            player_child_->AttachElement(layer_->element_id());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1:
                // First frame: add an animation to the root layer.
                AddAnimatedTransformToPlayer(player_.get(), 0.1, 5, 5);
                break;
            case 2:
                // Second frame: add an animation to the content layer. The root layer
                // animation has caused us to animate already during this frame.
                AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 5, 5, false);
                break;
            }
        }

        void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override
        {
            // After both animations have started, verify that they have valid
            // start times.
            if (host_impl->active_tree()->source_frame_number() < 2)
                return;

            // Animation state is updated after drawing. Only do this once.
            if (!TestEnded()) {
                ImplThreadTaskRunner()->PostTask(
                    FROM_HERE,
                    base::Bind(&LayerTreeHostAnimationTestAddAnimationAfterAnimating::
                                   CheckAnimations,
                        base::Unretained(this), host_impl));
            }
        }

        void CheckAnimations(LayerTreeHostImpl* host_impl)
        {
            GetImplTimelineAndPlayerByID(*host_impl);

            EXPECT_EQ(
                2u,
                GetImplAnimationHost(host_impl)->ticking_players_for_testing().size());

            Animation* root_anim = player_impl_->GetAnimation(TargetProperty::TRANSFORM);
            EXPECT_GT((root_anim->start_time() - base::TimeTicks()).InSecondsF(), 0);

            Animation* anim = player_child_impl_->GetAnimation(TargetProperty::OPACITY);
            EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);

            EndTest();
        }

        void AfterTest() override { }

    private:
        scoped_refptr<Layer> layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestAddAnimationAfterAnimating);

    class LayerTreeHostAnimationTestRemoveAnimation
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            layer_ = FakePictureLayer::Create(&client_);
            layer_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(layer_->bounds());
            layer_tree()->root_layer()->AddChild(layer_);

            AttachPlayersToTimeline();

            player_->AttachElement(layer_tree()->root_layer()->element_id());
            player_child_->AttachElement(layer_->element_id());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1:
                AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 5, 5);
                break;
            case 2:
                Animation* animation = player_child_->GetAnimation(TargetProperty::TRANSFORM);
                player_child_->RemoveAnimation(animation->id());
                gfx::Transform transform;
                transform.Translate(10.f, 10.f);
                layer_->SetTransform(transform);

                // Do something that causes property trees to get rebuilt. This is
                // intended to simulate the conditions that caused the bug whose fix
                // this is testing (the test will pass without it but won't test what
                // we want it to). We were updating the wrong transform node at the end
                // of an animation (we were assuming the layer with the finished
                // animation still had its own transform node). But nodes can only get
                // added/deleted when something triggers a rebuild. Adding a layer
                // triggers a rebuild, and since the layer that had an animation before
                // no longer has one, it doesn't get a transform node in the rebuild.
                layer_->AddChild(Layer::Create());
                break;
            }
        }

        void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override
        {
            LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id());
            switch (host_impl->active_tree()->source_frame_number()) {
            case 0:
                // No animation yet.
                break;
            case 1:
                // Animation is started.
                EXPECT_TRUE(child->screen_space_transform_is_animating());
                break;
            case 2: {
                // The animation is removed, the transform that was set afterward is
                // applied.
                gfx::Transform expected_transform;
                expected_transform.Translate(10.f, 10.f);
                EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
                    child->DrawTransform());
                EXPECT_FALSE(child->screen_space_transform_is_animating());
                EndTest();
                break;
            }
            default:
                NOTREACHED();
            }
        }

        void AfterTest() override { }

    private:
        scoped_refptr<Layer> layer_;
        FakeContentLayerClient client_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveAnimation);

    class LayerTreeHostAnimationTestIsAnimating
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            layer_ = FakePictureLayer::Create(&client_);
            layer_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(layer_->bounds());
            layer_tree()->root_layer()->AddChild(layer_);

            AttachPlayersToTimeline();
            player_->AttachElement(layer_->element_id());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1:
                AddAnimatedTransformToPlayer(player_.get(), 1.0, 5, 5);
                break;
            case 2:
                Animation* animation = player_->GetAnimation(TargetProperty::TRANSFORM);
                player_->RemoveAnimation(animation->id());
                break;
            }
        }

        void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id());
            switch (host_impl->sync_tree()->source_frame_number()) {
            case 0:
                // No animation yet.
                break;
            case 1:
                // Animation is started.
                EXPECT_TRUE(child->screen_space_transform_is_animating());
                break;
            case 2:
                // The animation is removed/stopped.
                EXPECT_FALSE(child->screen_space_transform_is_animating());
                EndTest();
                break;
            default:
                NOTREACHED();
            }
        }

        void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override
        {
            LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id());
            switch (host_impl->active_tree()->source_frame_number()) {
            case 0:
                // No animation yet.
                break;
            case 1:
                // Animation is started.
                EXPECT_TRUE(child->screen_space_transform_is_animating());
                break;
            case 2:
                // The animation is removed/stopped.
                EXPECT_FALSE(child->screen_space_transform_is_animating());
                EndTest();
                break;
            default:
                NOTREACHED();
            }
        }

        void AfterTest() override { }

    private:
        scoped_refptr<Layer> layer_;
        FakeContentLayerClient client_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating);

    class LayerTreeHostAnimationTestAnimationFinishesDuringCommit
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestAnimationFinishesDuringCommit()
            : signalled_(false)
        {
        }

        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            layer_ = FakePictureLayer::Create(&client_);
            layer_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(layer_->bounds());
            layer_tree()->root_layer()->AddChild(layer_);

            AttachPlayersToTimeline();

            player_->AttachElement(layer_tree()->root_layer()->element_id());
            player_child_->AttachElement(layer_->element_id());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1)
                AddAnimatedTransformToPlayer(player_child_.get(), 0.04, 5, 5);
        }

        void WillCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 2) {
                // Block until the animation finishes on the compositor thread. Since
                // animations have already been ticked on the main thread, when the commit
                // happens the state on the main thread will be consistent with having a
                // running animation but the state on the compositor thread will be
                // consistent with having only a finished animation.
                completion_.Wait();
            }
        }

        void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            switch (host_impl->sync_tree()->source_frame_number()) {
            case 1:
                PostSetNeedsCommitToMainThread();
                break;
            case 2:
                gfx::Transform expected_transform;
                expected_transform.Translate(5.f, 5.f);
                LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id());
                EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
                    layer_impl->DrawTransform());
                EndTest();
                break;
            }
        }

        void UpdateAnimationState(LayerTreeHostImpl* host_impl,
            bool has_unfinished_animation) override
        {
            if (host_impl->active_tree()->source_frame_number() == 1 && !has_unfinished_animation && !signalled_) {
                // The animation has finished, so allow the main thread to commit.
                completion_.Signal();
                signalled_ = true;
            }
        }

        void AfterTest() override { }

    private:
        scoped_refptr<Layer> layer_;
        FakeContentLayerClient client_;
        CompletionEvent completion_;
        bool signalled_;
    };

    // An animation finishing during commit can only happen when we have a separate
    // compositor thread.
    MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationFinishesDuringCommit);

    class LayerTreeHostAnimationTestNotifyAnimationFinished
        : public LayerTreeHostAnimationTest {
    public:
        LayerTreeHostAnimationTestNotifyAnimationFinished()
            : called_animation_started_(false)
            , called_animation_finished_(false)
        {
        }

        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            picture_ = FakePictureLayer::Create(&client_);
            picture_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(picture_->bounds());
            layer_tree()->root_layer()->AddChild(picture_);

            AttachPlayersToTimeline();
            player_->AttachElement(picture_->element_id());
            player_->set_animation_delegate(this);
        }

        void BeginTest() override
        {
            PostAddLongAnimationToMainThreadPlayer(player_.get());
        }

        void NotifyAnimationStarted(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            called_animation_started_ = true;
            layer_tree_host_in_process()->AnimateLayers(
                base::TimeTicks::FromInternalValue(
                    std::numeric_limits<int64_t>::max()));
            PostSetNeedsCommitToMainThread();
        }

        void NotifyAnimationFinished(base::TimeTicks monotonic_time,
            TargetProperty::Type target_property,
            int group) override
        {
            called_animation_finished_ = true;
            EndTest();
        }

        void AfterTest() override
        {
            EXPECT_TRUE(called_animation_started_);
            EXPECT_TRUE(called_animation_finished_);
        }

    private:
        bool called_animation_started_;
        bool called_animation_finished_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> picture_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestNotifyAnimationFinished);

    // Check that transform sync happens correctly at commit when we remove and add
    // a different animation player to an element.
    class LayerTreeHostAnimationTestChangeAnimationPlayer
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            AttachPlayersToTimeline();
            timeline_->DetachPlayer(player_child_.get());
            player_->AttachElement(layer_tree()->root_layer()->element_id());

            TransformOperations start;
            start.AppendTranslate(5.f, 5.f, 0.f);
            TransformOperations end;
            end.AppendTranslate(5.f, 5.f, 0.f);
            AddAnimatedTransformToPlayer(player_.get(), 1.0, start, end);
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            PropertyTrees* property_trees = host_impl->sync_tree()->property_trees();
            TransformNode* node = property_trees->transform_tree.Node(host_impl->sync_tree()
                                                                          ->root_layer_for_testing()
                                                                          ->transform_tree_index());
            gfx::Transform translate;
            translate.Translate(5, 5);
            switch (host_impl->sync_tree()->source_frame_number()) {
            case 2:
                EXPECT_TRANSFORMATION_MATRIX_EQ(node->local, translate);
                EndTest();
                break;
            default:
                break;
            }
        }

        void DidCommit() override { PostSetNeedsCommitToMainThread(); }

        void WillBeginMainFrame() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 2) {
                // Destroy player.
                timeline_->DetachPlayer(player_.get());
                player_ = nullptr;
                timeline_->AttachPlayer(player_child_.get());
                player_child_->AttachElement(layer_tree()->root_layer()->element_id());
                AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 10, 10);
                Animation* animation = player_child_->GetAnimation(TargetProperty::TRANSFORM);
                animation->set_start_time(base::TimeTicks::Now() + base::TimeDelta::FromSecondsD(1000));
                animation->set_fill_mode(Animation::FillMode::NONE);
            }
        }

        void AfterTest() override { }
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestChangeAnimationPlayer);

    // Check that SetTransformIsPotentiallyAnimatingChanged is called
    // if we destroy ElementAnimations.
    class LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            prev_screen_space_transform_is_animating_ = true;
            screen_space_transform_animation_stopped_ = false;

            LayerTreeHostAnimationTest::SetupTree();
            AttachPlayersToTimeline();
            player_->AttachElement(layer_tree()->root_layer()->element_id());
            AddAnimatedTransformToPlayer(player_.get(), 1.0, 5, 5);
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->pending_tree()->source_frame_number() <= 1) {
                EXPECT_TRUE(host_impl->pending_tree()
                                ->root_layer_for_testing()
                                ->screen_space_transform_is_animating());
            } else {
                EXPECT_FALSE(host_impl->pending_tree()
                                 ->root_layer_for_testing()
                                 ->screen_space_transform_is_animating());
            }
        }

        void DidCommit() override { PostSetNeedsCommitToMainThread(); }

        void UpdateLayerTreeHost() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 2) {
                // Destroy player.
                timeline_->DetachPlayer(player_.get());
                player_ = nullptr;
            }
        }

        DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
            LayerTreeHostImpl::FrameData* frame_data,
            DrawResult draw_result) override
        {
            const bool screen_space_transform_is_animating = host_impl->active_tree()
                                                                 ->root_layer_for_testing()
                                                                 ->screen_space_transform_is_animating();

            // Check that screen_space_transform_is_animating changes only once.
            if (screen_space_transform_is_animating && prev_screen_space_transform_is_animating_)
                EXPECT_FALSE(screen_space_transform_animation_stopped_);
            if (!screen_space_transform_is_animating && prev_screen_space_transform_is_animating_) {
                EXPECT_FALSE(screen_space_transform_animation_stopped_);
                screen_space_transform_animation_stopped_ = true;
            }
            if (!screen_space_transform_is_animating && !prev_screen_space_transform_is_animating_)
                EXPECT_TRUE(screen_space_transform_animation_stopped_);

            prev_screen_space_transform_is_animating_ = screen_space_transform_is_animating;

            return draw_result;
        }

        void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->active_tree()->source_frame_number() >= 2)
                EndTest();
        }

        void AfterTest() override
        {
            EXPECT_TRUE(screen_space_transform_animation_stopped_);
        }

        bool prev_screen_space_transform_is_animating_;
        bool screen_space_transform_animation_stopped_;
    };

    MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction);

    // Check that we invalidate property trees on AnimationPlayer::SetNeedsCommit.
    class LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit
        : public LayerTreeHostAnimationTest {
    public:
        void SetupTree() override
        {
            LayerTreeHostAnimationTest::SetupTree();
            layer_ = FakePictureLayer::Create(&client_);
            layer_->SetBounds(gfx::Size(4, 4));
            client_.set_bounds(layer_->bounds());
            layer_tree()->root_layer()->AddChild(layer_);

            AttachPlayersToTimeline();

            player_->AttachElement(layer_tree()->root_layer()->element_id());
            player_child_->AttachElement(layer_->element_id());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1 || layer_tree_host()->SourceFrameNumber() == 2)
                PostSetNeedsCommitToMainThread();
        }

        void UpdateLayerTreeHost() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                EXPECT_FALSE(layer_tree()->property_trees()->needs_rebuild);
                AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 5, 5);
            }

            EXPECT_TRUE(layer_tree()->property_trees()->needs_rebuild);
        }

        void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override
        {
            if (host_impl->active_tree()->source_frame_number() >= 2)
                EndTest();
        }

        void AfterTest() override { }

    private:
        scoped_refptr<Layer> layer_;
        FakeContentLayerClient client_;
    };

    MULTI_THREAD_TEST_F(
        LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit);

} // namespace
} // namespace cc
